/*file chiron src/test/base/deferrent.js */
/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/

include('http.js');
include('base.js');
include('time.js');
include('transitive.js');

/*** deferize
    takes a function returns a form of the function
    that is guarantees that it subscribes to the
    deferrent pattern.

    Deferrent functions have two calling conventions.
    The blocking form is not special, nor necessarily
    supported::
        
        var result = deferrent(arguments);
        // dependent code

    Deferrent functions also have a nonblocking
    form that accepts an additional callback argument
    that will accept the result as an argument::

        deferrent(arguments, function (result) {
            // dependent code
        });

    Functions subscribing to the notion of
    deferrence must be decorated with
    ``deferrent``.

    Most functions, like ``repr``, are designed
    without consideration for deferrence.
    Some functions, like ``delayed``, cannot support
    a blocking form.
    Some functions, like ``http.js#requestText``, 
    suport both nonblocking and blocking forms
    inherently.

    Some algorithms must use both kinds of 
    functions in chained succession generally.
    For these algorithms, the ``deferrent`` function
    will take any of these mentioned function
    classes and return a function that supports
    at least the nonblocking form, if not
    the blocking form as well.
    Also, some deferrent functions, like ``require``
    may respect the nonblocking calling convention,
    but in fact block.

    Deferrent functions can be used to create
    "continuation passing" algorithms that
    themselves are nonblocking deferrent::

        deferize(http.requestText)( // get text
            'url.txt',
            function (text) {
                deferize(name.upper)( // to upper-case
                    text,
                    function (text) {
                        deferize(delayed)( // sleep 1
                            1000,
                            function () {
                                // dependent code
                            }
                        })
                    }
                );
            }
        });


*/
exports.deferize = function (continuation) {
    if (continuation.deferrent === true) return continuation;
    var decorated = function () {
        var args = List(arguments);
        if (isInstance(args.last(), Function)) {
            var callback = args.pop();
            callback(continuation.apply(this, args.to(array)));
        } else {
            return continuation.apply(this, args.to(array));
        }
    };
    decorated.deferrent = true;
    return decorated;
};

/*** deferrent
    decorates a function such that deferize will
    recognize that the given function is inherently
    deferrent.
*/
exports.deferrent = function (continuation) {
    continuation.deferrent = true;
    return continuation;
};

/*** pipe
    returns a function composed of the given functions such
    that: ``pipe([f1, f2, ..., fn])(a...z) == fn(...f1(f1(a...z)))``
*/
exports.pipe = function (continuations) {
    var continuations = list(continuations);
    return function () {
        var length = continuations.len();
        var args = arguments;
        for (var i = 0; i < length; i++) {
            var continuation = continuations.get(i);
            args = [continuation.apply(this, args)];
        }
        return args[0];
    };
};

/*** series
*/
exports.series = function () {
    var continuations = arguments.length == 1? arguments[0] : arguments;
    continuations = iter(continuations);
    var step = function (input) {
        try {
            var continuation = continuations.next();
            deferize(continuation)(input, step);
        } catch (exception) {
            if (isInstance(exception, StopIteration)) {
            } else {
                throw exception;
            }
        }
    };
    return step;
};



deferize(add)(1, 2, function (result) {
    log(result);
});
deferize(sub)(1, 2, function (result) {
    log(result);
});

log(
    pipe(map(deferize, [
        add,
        neg,
        string,
        function (n) {return 'hello ' + n},
        upper
    ]))(10, 20)
);

log(deferize(neg)(10));
series(
    neg,
    partial(add, 10),
    partial(add, 10),
    partial(add, -5),
    partial(mul, 2),
    function (n) {
        log(n);
    }
)(10);

series(
    requestText,
    function (text) {
        log(text);
        return text;
    },
    upper,
    deferrent(function (text, continuation) {
        log('ding');
        delayed(1000, function () {
            continuation(text);
        });
    }),
    log
)(resolve('deferrent.js', moduleUrl));


/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

